/*
 * ApplicationLayer.cpp
 *
 *  @author John Manero (jmanero@wpi.edu)
 *  @version April 11, 2012
 */
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/time.h>
#include <sys/types.h>
using std::cout;
using std::cerr;
using std::endl;
using std::string;

#include "ApplicationLayer.h"

ApplicationLayer::ApplicationLayer(bool server) {
	if (server) { // Server mode. Connect to back-end application
		cout << "  Starting Server application" << endl;
		struct sockaddr_in servAddr;

		// Construct the server address structure
		memset(&servAddr, 0, sizeof(servAddr));
		servAddr.sin_family = AF_INET;
		servAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
		servAddr.sin_port = htons(APPSERVER_PORT);

		// Create the TCP socket
		if ((this->sockId = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
			cerr << "Client: Error with socket()" << endl;
			exit(1);
		}

		// Connect to the server
		if (connect(this->sockId, (struct sockaddr *) &servAddr,
				sizeof(servAddr)) < 0) {
			cerr << "  Client: Error with connect()" << endl;
			exit(1);
		}
		cout << "Connected to app server" << endl;

	} else { // Client mode. Listen for connections
		cout << "  Starting Client application" << endl;
		struct sockaddr_in servAddr;
		int servID;

		// Create the TCP socket
		if ((servID = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
			cerr << "Server: Error with socket()" << endl;
			exit(1);
		}

		// Construct the server address structure
		memset(&servAddr, 0, sizeof(servAddr));
		servAddr.sin_family = AF_INET;
		servAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
		servAddr.sin_port = htons(APPCLIENT_PORT);

		// Set socket to be able to reuse this address
		setsockopt(servID, SOL_SOCKET, SO_REUSEADDR, (const void *) 1, 4);

		// Bind socket to the server address
		if (bind(servID, (struct sockaddr *) &servAddr, sizeof(servAddr)) < 0) {
			cerr << "Server: Error with bind()" << endl;
			exit(1);
		}

		// Allow socket to listen for connections
		if (listen(servID, 100) < 0) {
			cerr << "Server: Error with listen()" << endl;
			exit(1);
		}
		cout << "  Listening for client requests" << endl;

		while (true) {
			// Accept a client connection
			if ((this->sockId = accept(servID, NULL, NULL)) < 0) {
				cerr << "Server: Error with accept()" << endl;
				exit(1);
			}

			// Fork new process for new connection
			int pid = fork();

			// Return if we are the child
			if (pid == 0) {
				cout << "  New client connected to Application" << endl;
				return;
			} else if (pid == -1) {
				cerr << "Server: Error with fork()" << endl;
				exit(1);
			}
		}
	}

}

void ApplicationLayer::run() {
	while (1) {
		this->receiveData();
		this->sendData();
	}
}

// Send data to the application thingy
// SEND IS RELATIVE TO SOCKET (opposite to PhysicalLayer)
void ApplicationLayer::sendData() {
	cout << "Run like an antelope" << endl;
	string data = string();

	while (true) { // Aggregate fragments back into original string
		// This will not handle sequence faults!

		// Try to get lock (don't want to block)
		if (pthread_mutex_trylock(&recvPktM) == 0) {
			if (!recvPktQ.empty()) {
				data += string(recvPktQ.front()->data);
				cout << "    Adding cell to application data" << recvPktQ.front()->data << endl;

				if (recvPktQ.front()->end) {
					recvPktQ.pop();
					pthread_mutex_unlock(&recvPktM);
					break;
				}

				recvPktQ.pop();
			} else {
				cout << "breaking, recvPktQ empty" << endl;
				pthread_mutex_unlock(&recvPktM);
				break;
			}
			pthread_mutex_unlock(&recvPktM);
		} else {
			cout << "breaking, no lock" << endl;
			break;
		}
	}

	if(data.length() > 0) {
		cout << "    Sending data to application: " << data << endl;
		if (send(this->sockId, data.c_str(), data.length(), 0)
				!= (int) data.length()) {
			cerr << "AL: Error with send()" << endl;
		}
	}
}

void ApplicationLayer::receiveData() {
	Packet *recvPacket;

	struct timeval timeout;
	timeout.tv_sec = 0;
	timeout.tv_usec = 100; // Might not need to wait this long

	fd_set set;
	FD_ZERO(&set);
	FD_SET(this->sockId, &set);

	// Use select() so recv() won't block
	while (select(this->sockId + 1, &set, 0, 0, &timeout)) {
		if (FD_ISSET(this->sockId, &set)) {
			cout << "Got a packet from application side" << endl;
			int len = MAX_PACKET_SZ;
			char *data = (char *) malloc((len + 1) * sizeof(char));

			// Read the frame
			if ((len = recv(this->sockId, data, len, 0)) <= 0) {
				exit(1); // Assume we disconnected
			}
			data[len] = '\0';

			recvPacket = new Packet();
			recvPacket->length = len;
			if (len >= MAX_PACKET_SZ) {
				recvPacket->end = false;
			} else {
				recvPacket->end = true;
			}

			recvPacket->data = (char *) malloc(
					(recvPacket->length + 1) * sizeof(char));
			memcpy(recvPacket->data,
					(const char *) (data), recvPacket->length);
			recvPacket->data[recvPacket->length] = '\0';

			pthread_mutex_lock(&sendPktM);
			cout << "    Added packet to sendQ" << endl;
			sendPktQ.push(recvPacket);
			pthread_mutex_unlock(&sendPktM);

		}

		// Reset our bitset
		FD_ZERO(&set);
		FD_SET(this->sockId, &set);
	}
}

ApplicationLayer::~ApplicationLayer() {
	close(this->sockId);
}

